Workflow Engine

On this page:

Job Control Workflow Tasks

childJob

Some jobs share a need for the same set of task sequences. For this situation, you can create a separate workflow containing a single instance of those tasks and call them from other jobs.

Purpose

The childJob task starts a job inside the running job. The calling or parent job waits for the child to finish (complete or cancel) before progressing to its next task. If the child job is cancelled, the childJob task will error and progress to the next task along its error transition. Anything that is a job variable in the child job is placed in an object with the <job_variable__name> : <job_variable_value> key/value pair format and is returned as the outgoing data from the child job task to the parent. Then if you want a particular job variable, you use a query task to query out that key (job variable name) and now you have your value.

Properties

Property Description
Workflow (required) Select a child workflow in the Workflow dropdown.
Job Variables When the child job requires job variables, those variables will be listed in the Job Variables section. The name of each job variable will be listed with Location and Value fields. Specify where to find values for each child's job variables in the Location dropdown. Values can come from a job variable, a static value, or an earlier task's outgoing variable. If the Location is a job variable or earlier task, the Value shows a dropdown of available variable names for selection. If the Location is static, the Value variable becomes a text edit box.

Job Variables

Example

Figure: Child Job task

Child Job Task

Delay

Purpose

The delay task pauses a job for a specified number of seconds. Some jobs require a delay to allow for network convergence, database commits, or other external system processing.

Properties

Incoming Description
time (required) Specify the length of time in seconds to pause a job. Hover the cursor over the information icon to see a tool tip help message. The value can come from a job variable, a static value, or an earlier task's outgoing variable. If the reference task is a job variable or earlier task, the reference variable shows a dropdown of available variable names for selection. If the reference task is static, the reference variable becomes a text edit box where we can type the number of seconds to delay.
Outgoing Description
time_in_milliseconds The delay task converts incoming variable time to milliseconds and assigns the value to outgoing variable time_in_milliseconds. You may create a job variable from time_in_milliseconds. The immutable outgoing variable time_in_milliseconds remains, and a new mutable job variable is created. When creating a job variable, provide a new name to avoid name conflicts and provide clarity.

Evaluation

Purpose

The evaluation task controls a job's execution path depending on run-time conditions. Any single evaluation task will send a job along one of two possible directions by evaluating a condition as either true or false.

  • When an evaluation task derives a true result, the job will follow transitions bound to the evaluation task's success finish state.
  • When an evaluation task derives a false result, the job will follow transitions bound to the evaluation task's failure finish state.

Figure: Evaluation Task

Evaulation Task

Properties

Single Evaluation

An evaluation is a single expression consisting of a left operand, a comparison operator, and a right operand. The left operand is compared to the right operand yielding either true or false.

  1. Select the Location and Value for the left operand.
    • It may come from a job variable, a static value, or an earlier task's outgoing variable.
    • If the operand location is a job variable or earlier task, the operand value shows a dropdown of available variable names for selection.
    • If the operand location is static, the operand value becomes a text edit box where you can type a value.
  2. Enter a query in the Query field. Query syntax follows that of the workflow operation query task. The left Operand Location and Operand Value fields define the object to query, the Query field specifies the query to execute, and the query result becomes our left operand.
  3. Select the Location and Value of the right operand.
    • It may come from a job variable, a static value, or an earlier task's outgoing variable.
    • If the operand location is a job variable or earlier task, the operand value shows a dropdown of available variable names for selection.
    • If the operand location is static, the operand value becomes a text edit box where you can type a value.

runEvaluation Tasks

Operators are overloaded to match the data types they compare. The following table displays how the Workflow (WF) evaluation tasks function on a per data type basis.

Left Operand Data Type Operator Right Operand Data Type Comparison
string contains string True if right operand is a sub-string of left operand.
boolean contains boolean There is no contains evaluation for booleans so this will do equality tests (true if left operand == right operand).
number contains number Evaluate to true based on whether the modulus operation returns a remainder or not. If there is a remainder, it returns false; it returns true otherwise. Examples: Left operand = 4 and Right operand =2 returns true because 4%2=0 Left operand = 5 and right operand = 2 returns false because 5%2=1
array contains string boolean number True if the right operand is an element in the left operand array.
array contains object array Contains uses the inherent indexOf method so these always return false due to how arrays handle complex objects.
object contains string If no "query" parameter is sent in with the evaluation, this will check if the there is a property in the left operand that contains the text of the right operand Example: left Operand : {"color": "red"}, right operand: "col" : this will return true because there is a property name "color" in the left operand that contains the value "col"
object contains string number object array If a "query" parameter is sent in with the evaluation, it will check if there is a property name in the left operand with the provided query value and check whether its value contains the right operand. Example: left operand: {"color" : "reddish-brown"}, query: "color" right operand: "red" This will return true because there is a property in the left operand named "color" and it's value (reddish-brown) contains the right operand value (red).
Left Operand Data Type Operator Right Operand Data Type Comparison
string !contains string True if the right operand is not a sub-string of left operand.
boolean !contains boolean There is no !contains evaluation for booleans so this will do inequality tests (true if left operand != right operand).
number !contains number Evaluate to true based on whether the modulus operation returns a remainder or not. If there is a remainder, it returns true; returns false otherwise. Examples: Left operand = 4 and Right operand =2 returns false because 4%2=0 Left operand = 5 and right operand = 2 returns true because 5%2=1
array !contains string boolean number False if the right operand is an element in the left operand array.
array !contains object array Contains uses the inherent indexOf method so these always return false due to how arrays handle complex objects.
object !contains string If no "query" parameter is sent in with the evaluation, this will check if the there is a property in the left operand that contains the text of the right operand. Example: left Operand : {"color": "red"}, right operand: "col" : this will return false because there is a property name "color" in the left operand that contains the value "col" left Operand : {"color": "red"}, right operand: "abcd" : this will return true because there is no property in the left operand with a name that contains the value "abcd"
object !contains string number object array If a "query" parameter is sent in with the evaluation, it will check if there is a property name in the left operand with the provided query value and check whether its value contains the right operand. Example: left operand: {"color" : "reddish-brown"}, query: "color" right operand: "blue" This will return true because there is a property in the left operand named "color" and it's value (reddish-brown) does not contain the right operand value (blue).
Left Operand Data Type Operator Right Operand Data Type Comparison
string > string Does evaluations based on lexicographical ordering. See Wikipedia - Lexicographical Order for details on how lexicographical ordering works. Examples: "aa" > "ab" returns false "ab" > "aa" returns true
boolean > boolean True is always > than false.
number > number Returns true if the left operand is > the right operand.
array > number Returns true if the number in the right operand is > the length of the left operand array.
array > array Returns true if the length of the left operand array is > the length of the right operand array.
object > any data type Always returns false.
Left Operand Data Type Operator Right Operand Data Type Comparison
string < string Does evaluations based on lexicographical ordering. See Wikipedia - Lexicographical Order for details on how lexicographical ordering works. Examples: "aa" < "ab" returns true "ab" < "aa" returns false
boolean < boolean False is always < true
number < number Returns true if the left operand value is < the right operand value.
array < number Returns true if the number in the right operand is < the length of the left operand array.
array < array Returns true if the length of the left operand array is < the length of the right operand array.
object < any data type Always returns false.
Left Operand Data Type Operator Right Operand Data Type Comparison
string == string Returns true if the 2 strings are equal.
boolean == boolean Returns true if the 2 booleans are equal.
number == number Returns true if the 2 numbers are equal.
array == number Returns true if the right operand number is the exact length of the left operand array.
array == array Returns true if the length of the right operand array is the exact length of the left operand array.
object == object Returns true if the 2 objects are deeply equal (same properties and values).
Left Operand Data Type Operator Right Operand Data Type Comparison
string != string Returns true if the 2 strings are not equal.
boolean != boolean Returns true if the 2 booleans are not equal.
number != number Returns true if the 2 numbers are not equal.
array != number Returns true if the right operand number is not the exact length of the left operand array.
array != array Returns true if the length of the right operand array is not the exact length of the left operand array.
object != object Returns true if the 2 objects are not deeply equal (same properties and values).
Left Operand Data Type Operator Right Operand Data Type Comparison
string >= string Does evaluations based on lexicographical ordering. Wikipedia - Lexicographical Order for details on how lexicographical ordering works Examples: "aa" >= "aa" returns true because "aa" is not > "aa" but it is equal "ab" >= "aa" returns true
boolean >= boolean True is always > false.
number >= number Returns true if the left operand is >= the right operand.
array >= number Returns true if the right operand number is >= the length of the left operand array.
array >= array Returns true if the length of the right operand array is >= the length of the left operand array.
object >= object Always returns false.
Left Operand Data Type Operator Right Operand Data Type Comparison
string <= string Does evaluations based on lexicographical ordering. See Wikipedia - Lexicographical Order for details on how lexicographical ordering works Examples: "aa" <= "aa" returns true because "aa" is not < "aa" but it is equal "aa" <= "ab" returns true
boolean <= boolean True is always > false.
number <= number Returns true if the left operand is <= the right operand.
array <= number Returns true if the right operand number is <= the length of the left operand array.
array <= array Returns true if the length of the right operand array is <= the length of the left operand array.
object <= object Always returns false

Evaluation Groups

  • One or more evaluations can be combined into an evaluation group. The evaluation group resolves to true or false by logically combining its evaluations' results.
  • Specify how to combine evaluations in the group's "If [any/all] of the following are met" setting.
  • When set to any, the evaluation group is true if one or more of its evaluations have a true condition.
  • When set to all, the evaluation group is true only if all of its evaluations have a true condition.

Final Evaluation

  • The evaluation task resolves to true or false by logically combining its evaluation groups.
  • Specify how to combine evaluation groups in the task's "If [any/all] of the following are met" setting.
  • When set to any, the evaluation task is true if one or more of its evaluation groups have a true condition.
  • When set to all, the evaluation task is true only if all of its evaluations groups have a true condition.

The following table provides sample evaluation conditions and the result.

Left Operand Operator Right Operand Results
"er1.atl" contains "atl" T
"er1.atl" contains "ATL" F
[ "cr1.atl", "er1.atl" ] contains "er1.atl" T
[ "cr1.atl", "er1.atl" ] contains "atl" F
{ "name": "apple", "color": "red" } contains "color" T
{ "name": "apple", "color": "red" } contains "apple" F
4 > 2 T
1 > 2 F
true > false T
true > 0 F
1 > false F
false > true F
"chassis" > "card" error
"chassis" > 4 F
"code" > "card" error
"code" > "17" F
[ "cr1.atl", "er1.atl" ] > 1 T
[ "cr1.atl", "er1.atl" ] > [ "cr2.atl" ] T
[ "cr1.atl", "er1.atl" ] > 3 F
6 < 9 T
9 < 6 F
false < true T
false < TRUE T
false < 5 F
true < false F
"card" < "chassis" error
"card" < 5 F
"code" < "card" error
[ "cr1.atl", "er1.atl" ] < 3 T
[ "cr2.atl" ] < [ "cr1.atl", "er1.atl" ] T
[ "cr1.atl", "er1.atl" ] < 1 F
6 == 6 T
6 == "6" T
6 == 7 F
false == false T
false == FALSE T
false == "FALSE" T
true == false F
"card" == "card" T
"card" == "chassis" F
"card" == "code" F
[ "cr1.atl", "er1.atl" ] == [ "cr1.atl", "er1.atl" ] T
[ "cr1.atl", "er1.atl" ] == [ "er1.atl", "cr1.atl" ] T
[ "cr1.atl", "er1.atl" ] == [ "cr1.atl", "cr2.atl" ] T
[ "cr1.atl", "er1.atl" ] == [ "cr1.atl" ] F
[ "cr1.atl", "er1.atl" ] == [ "cr1.atl", "cr1.atl", "cr1.atl" ] F
{ "name": "cr1.atl", "ned": "cisco-ios-xr", "slots" = 8 } == { "name": "cr1.atl", "ned": "cisco-ios-xr", "slots" = 8 } T
{ "name": "cr1.atl", "ned": "cisco-ios-xr", "slots" = 8 } == { "name": "cr1.atl", "ned": "cisco-ios-xr", "slots" = "8" } F
{ "name": "cr1.atl", "ned": "cisco-ios-xr", "slots" = 8 } == { "name": "cr2.atl", "ned": "cisco-ios-xr", "slots" = 8 } F
6 != 7 T
6 != 6 F
6 != "6" F
true != false T
false != false F
false != "FALSE" F
"card" != "chassis" T
"card" != "code" T
"card" != "card" F
[ "cr1.atl", "er1.atl" ] != [ "cr1.atl", "cr2.atl" ] F
[ "cr1.atl", "er1.atl" ] != [ "er1.atl", "cr1.atl" ] F
[ "cr1.atl", "er1.atl" ] != [ "cr1.atl", "er1.atl" ] F
[ "cr1.atl", "er1.atl" ] != [ "cr1.atl" ] T
[ "cr1.atl", "er1.atl" ] != [ "cr1.atl", "cr1.atl", "cr1.atl" ] T
{ "name": "cr1.atl", "ned": "cisco-ios-xr", "slots" = 8 } != { "name": "cr2.atl", "ned": "cisco-ios-xr", "slots" = 8 } T
{ "name": "cr1.atl", "ned": "cisco-ios-xr", "slots" = 8 } != { "name": "cr1.atl", "ned": "cisco-ios-xr", "slots" = 8 } F
{ "name": "cr1.atl", "ned": "cisco-ios-xr", "slots" = 8 } != { "name": "cr1.atl", "ned": "cisco-ios-xr", "slots" = "8" } T
4 >= 4 T
1 >= 2 F
true >= true T
false >= true F
"FALSE" >= true F
"chassis" >= "card" error
"code" >= "card" error
"code" >= 4 F
"card" >= "chassis" error
[ "cr1.atl", "er1.atl" ] >= 1 T
[ "cr1.atl", "er1.atl" ] >= [ "cr2.atl" ] T
[ "cr1.atl", "er1.atl" ] >= 3 F
6 <= 9 T
9 <= 6 F
false <= true T
FALSE <= "TruE" T
false <= 0 F
true <= false F
true <= "FALsE" F
"card" <= "chassis" error
"code" <= "card" error
"code" <= 3 F
"code" <= 9 F
"code" <= "3" F
[ "cr1.atl", "er1.atl" ] <= 3 T
[ "cr1.atl", "er1.atl" ] <= 1 F
[ "cr1.atl", "er1.atl" ] <= [ "cr2.atl", "cr3.atl", "cr4.atl" ] T
[ "cr1.atl", "er1.atl" ] <= [ "cr2.atl" ] F

forEach

Purpose

The forEach task executes a loop inside a job. It iterates over a passed array, and for each element, it executes a series of tasks. The forEach task is helpful when using tasks that operate on one thing, such as a device, but then you need to repeat those steps over a set of devices. Building a loop in the workflow is more efficient than writing new tasks.

The forEach task is unique as it supports transitions that follow the loop value. When a transition is created to follow the loop, it is distinguished with a blue arrow. The series of tasks at the end of the blue arrow then dictates what will be executed for every iteration of the forEach task. A forEach task also supports transitions bound to the success and error finish states. When forEach completes its iterations, barring any errors it returns with a success finish state, and the job then follows the transition(s) bound to that finish state.

Properties

Direction Property Description
Incoming data_array (required) Specify the array of items forEach will use in its iterations. The value can come from a job variable, a static value, or an earlier task's outgoing variable. If the reference task is a job variable or earlier task, the reference variable shows a dropdown of available variable names for selection. If the reference task is static, the reference variable becomes a text edit field.
Outgoing current_item In each iteration, the forEach task copies the next data_array element's value into its outgoing variable current_item. You may create a job variable from current_item. The immutable outgoing variable current_item remains, and a new mutable job variable is created. When creating a job variable, provide a new name to avoid name conflicts and provide clarity.

Examples

Replace Retired Device Group

Use the forEach workflow operation task for a job that replaces a retired device group used on every device with a new group.

forEach Example

When the job begins, the first automatic task returns a list of all devices. The device list is passed to a forEach task with executes four tasks for every device passed. The four (4) tasks in the example above remove each device from the retired device group, adds each device to the new device group, then concurrently gets any configuration on the device not in sync with its modeled configuration while getting all groups for the device. After the last device is processed, the forEach task returns with a success finish state, and the job completes.

Handling Errors Inside forEach Loops

Use the following information to handle errors inside forEach loops.

Handle Errors forEach loop

  1. Create newVariable.
  2. Name variable as saved_errors (empty array).
  3. Inside the forEach loop, assume you want to track errors for generic "task X".
  4. Create a push task to transition saved_errors from "task X" to the push task.
  5. Push the errors into the array.
  6. Query the job variable and evaluate if the length is 0 or not.